local L = LibStub("AceLocale-3.0"):GetLocale("IceHUD", false)
IceClassPowerCounter = IceCore_CreateClass(IceElement)

local IceHUD = _G.IceHUD

IceClassPowerCounter.prototype.runeHeight = 22
IceClassPowerCounter.prototype.runeWidth = 36
IceClassPowerCounter.prototype.numRunes = 3
IceClassPowerCounter.prototype.lastNumReady = 0
IceClassPowerCounter.prototype.runeCoords = {}
IceClassPowerCounter.prototype.runeShineFadeSpeed = 0.4
IceClassPowerCounter.prototype.minLevel = 9

-- Constructor --
function IceClassPowerCounter.prototype:init(name)
	assert(name ~= nil, "ClassPowerCounter cannot be instantiated directly - supply a name from the child class and pass it up.")
	IceClassPowerCounter.super.prototype.init(self, name)

	self.scalingEnabled = true
end

-- 'Public' methods -----------------------------------------------------------


-- OVERRIDE
function IceClassPowerCounter.prototype:GetOptions()
	local opts = IceClassPowerCounter.super.prototype.GetOptions(self)

	opts["vpos"] = {
		type = "range",
		name = L["Vertical Position"],
		desc = L["Vertical Position"],
		get = function()
			return self.moduleSettings.vpos
		end,
		set = function(info, v)
			self.moduleSettings.vpos = v
			self:Redraw()
		end,
		min = -300,
		max = 700,
		step = 1,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		order = 31
	}

	opts["hpos"] = {
		type = "range",
		name = L["Horizontal Position"],
		desc = L["Horizontal Position"],
		get = function()
			return self.moduleSettings.hpos
		end,
		set = function(info, v)
			self.moduleSettings.hpos = v
			self:Redraw()
		end,
		min = -500,
		max = 500,
		step = 1,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		order = 31
	}

	opts["hideBlizz"] = {
		type = "toggle",
		name = L["Hide Blizzard Frame"],
		desc = L["Hides Blizzard frame and disables all events related to it.\n\nNOTE: Blizzard attaches this UI to the player's unitframe, so if you have that hidden in PlayerHealth, then this won't do anything."],
		get = function()
			return self.moduleSettings.hideBlizz
		end,
		set = function(info, value)
			self.moduleSettings.hideBlizz = value
			if (value) then
				self:HideBlizz()
			else
				self:ShowBlizz()
			end
		end,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		order = 32
	}

	opts["displayMode"] = {
		type = 'select',
		name = L["Display mode"],
		desc = L["Choose whether you'd like a graphical or numeric representation of the runes.\n\nNOTE: The color of 'Numeric' mode can be controlled by the HolyPowerNumeric color."],
		get = function(info)
			return IceHUD:GetSelectValue(info, self.moduleSettings.runeMode)
		end,
		set = function(info, v)
			self.moduleSettings.runeMode = info.option.values[v]
			self:SetDisplayMode()
			self:UpdateRunePower()
		end,
		values = { "Graphical", "Numeric", "Graphical Bar", "Graphical Circle", "Graphical Clean Circle", "Graphical Glow" },
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		order = 34
	}

	opts["runeGap"] = {
		type = 'range',
		name = L["Rune gap"],
		desc = L["Spacing between each rune (only works for graphical mode)"],
		min = 0,
		max = 100,
		step = 1,
		get = function()
			return self.moduleSettings.runeGap
		end,
		set = function(info, v)
			self.moduleSettings.runeGap = v
			self:Redraw()
		end,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		hidden = function()
			return self.moduleSettings.runeMode == "Numeric"
		end,
		order = 34.1
	}

	opts["runeOrientation"] = {
		type = 'select',
		name = L["Rune orientation"],
		desc = L["Whether the runes should draw side-by-side or on top of one another"],
		get = function(info)
			return IceHUD:GetSelectValue(info, self.moduleSettings.displayMode)
		end,
		set = function(info, v)
			self.moduleSettings.displayMode = info.option.values[v]
			self:Redraw()
		end,
		values = { "Horizontal", "Vertical" },
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		hidden = function()
			return self.moduleSettings.runeMode == "Numeric"
		end,
		order = 35
	}

	opts["inactiveDisplayMode"] = {
		type = 'select',
		name = L["Inactive mode"],
		desc = L["This controls what happens to runes that are inactive. Darkened means they are visible but colored black, Hidden means they are not displayed."],
		get = function(info)
			return IceHUD:GetSelectValue(info, self.moduleSettings.inactiveDisplayMode)
		end,
		set = function(info, v)
			self.moduleSettings.inactiveDisplayMode = info.option.values[v]
			self:SetDisplayMode()
			self:UpdateRunePower()
		end,
		values = { "Darkened", "Hidden" },
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		hidden = function()
			return self.moduleSettings.runeMode == "Numeric"
		end,
		order = 36
	}

	opts["flashWhenReady"] = {
		type = "toggle",
		name = L["Flash when ready"],
		desc = L["Shows a flash behind each holy rune when it becomes available."],
		get = function()
			return self.moduleSettings.flashWhenBecomingReady
		end,
		set = function(info, value)
			self.moduleSettings.flashWhenBecomingReady = value
		end,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		hidden = function()
			return self.moduleSettings.runeMode == "Numeric"
		end,
		order = 37
	}

	opts["customColor"] = {
		type = 'color',
		name = L["Custom color"],
		desc = L["The color for this counter"],
		get = function()
			return self:GetCustomColor()
		end,
		set = function(info, r,g,b)
			self.moduleSettings.customColor.r = r
			self.moduleSettings.customColor.g = g
			self.moduleSettings.customColor.b = b
			self:UpdateRunePower()
		end,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		hidden = function()
			return self.moduleSettings.runeMode == "Numeric" or self.moduleSettings.runeMode == "Graphical"
		end,
		order = 38,
	}

	opts["customMinColor"] = {
		type = 'color',
		name = L["Custom minimum color"],
		desc = L["The minimum color for this counter (only used if Change Color is enabled)"],
		get = function()
			return self:GetCustomMinColor()
		end,
		set = function(info, r,g,b)
			self.moduleSettings.customMinColor.r = r
			self.moduleSettings.customMinColor.g = g
			self.moduleSettings.customMinColor.b = b
			self:UpdateRunePower()
		end,
		disabled = function()
			return not self.moduleSettings.enabled or not self.moduleSettings.gradient
		end,
		hidden = function()
			return self.moduleSettings.runeMode == "Numeric" or self.moduleSettings.runeMode == "Graphical"
		end,
		order = 39,
	}

	opts["gradient"] = {
		type = "toggle",
		name = L["Change color"],
		desc = L["This will fade the graphical representation from the min color specified to the regular color\n\n(e.g. if the min color is yellow, the color is red, and there are 3 total applications, then the first would be yellow, second orange, and third red)"],
		get = function()
			return self.moduleSettings.gradient
		end,
		set = function(info, v)
			self.moduleSettings.gradient = v
			self:UpdateRunePower()
		end,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		hidden = function()
			return self.moduleSettings.runeMode == "Numeric" or self.moduleSettings.runeMode == "Graphical"
		end,
		order = 40,
	}

	opts["pulseWhenFull"] = {
		type = "toggle",
		name = L["Pulse when full"],
		desc = L["If this is checked, then whenever the counter is maxed out it will gently pulsate to let you know it's ready to be used."],
		get = function()
			return self.moduleSettings.pulseWhenFull
		end,
		set = function(info, v)
			self.moduleSettings.pulseWhenFull = v
			if v and self.lastNumReady == self.numRunes then
				self:StartRunesFullAnimation()
			else
				self:StopRunesFullAnimation()
			end
		end,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		order = 41,
	}

	opts["hideFriendly"] = {
		type = "toggle",
		name = L["Friendly OOC alpha"],
		desc = L["If this is checked, then the counter will use your 'out of target' alpha when targeting someone who is friendly."],
		get = function()
			return self.moduleSettings.hideFriendly
		end,
		set = function(info, v)
			self.moduleSettings.hideFriendly = v
			self:Update(self.unit)
		end,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		order = 42,
	}

	opts["overrideAlpha"] = {
		type = "toggle",
		name = L["Override alpha when not full"],
		desc = L["If your class power is not full (or not empty in the case of Holy Power) then the module will always be displayed on your screen using the In Combat alpha setting. Otherwise it will fade to the OOC alpha when you leave combat."],
		width = "double",
		get = function()
			return self.moduleSettings.overrideAlpha
		end,
		set = function(info, v)
			self.moduleSettings.overrideAlpha = v
			self:UpdateAlpha()
		end,
		disabled = function()
			return not self.moduleSettings.enabled
		end,
		order = 43,
	}

	return opts
end


-- OVERRIDE
function IceClassPowerCounter.prototype:GetDefaultSettings()
	local defaults =  IceClassPowerCounter.super.prototype.GetDefaultSettings(self)

	defaults["vpos"] = 0
	defaults["hpos"] = 10
	defaults["runeFontSize"] = 20
	defaults["runeMode"] = "Graphical"
	defaults["usesDogTagStrings"] = false
	defaults["hideBlizz"] = false
	defaults["alwaysFullAlpha"] = false
	defaults["displayMode"] = "Horizontal"
	defaults["runeGap"] = 0
	defaults["flashWhenBecomingReady"] = true
	defaults["inactiveDisplayMode"] = "Darkened"
	defaults["gradient"] = true
	defaults["customMinColor"] = {r=1, g=1, b=0, a=1}
	defaults["customColor"] = {r=1, g=0, b=0, a=1}
	defaults["hideFriendly"] = false
	defaults["pulseWhenFull"] = true
	defaults["overrideAlpha"] = true

	return defaults
end


-- OVERRIDE
function IceClassPowerCounter.prototype:Redraw()
	IceClassPowerCounter.super.prototype.Redraw(self)

	self:CreateFrame()
	self:UpdateRunePower()
end

function IceClassPowerCounter.prototype:GetCustomColor()
	return self.moduleSettings.customColor.r, self.moduleSettings.customColor.g, self.moduleSettings.customColor.b, self.alpha
end

function IceClassPowerCounter.prototype:GetCustomMinColor()
	return self.moduleSettings.customMinColor.r, self.moduleSettings.customMinColor.g, self.moduleSettings.customMinColor.b, self.alpha
end


-- OVERRIDE
function IceClassPowerCounter.prototype:Enable(core)
	IceClassPowerCounter.super.prototype.Enable(self, core)

	self:CheckValidLevel(nil, UnitLevel("player"))
end

function IceClassPowerCounter.prototype:CheckValidLevel(event, level)
	if not level then
		return
	end

	if level < self.minLevel then
		self:RegisterEvent("PLAYER_LEVEL_UP", "CheckValidLevel")
		self:Show(false)
	else
		self:DisplayCounter()
		self:Show(true)
	end
end

function IceClassPowerCounter.prototype:DisplayCounter()
	self:UnregisterEvent("PLAYER_LEVEL_UP")

	self:RegisterEvent("UNIT_POWER", "UpdateRunePower")
	self:RegisterEvent("UNIT_DISPLAYPOWER", "UpdateRunePower")
	self:RegisterEvent("PLAYER_ENTERING_WORLD", "UpdateRunePower")

	if (self.moduleSettings.hideBlizz) then
		self:HideBlizz()
	end

	self:UpdateRunePower()
end

function IceClassPowerCounter.prototype:Disable(core)
	IceClassPowerCounter.super.prototype.Disable(self, core)

	if self.moduleSettings.hideBlizz then
		self:ShowBlizz()
	end
end

function IceClassPowerCounter.prototype:UpdateRunePower()
	local numReady = UnitPower("player", self.unitPower)

	if self.moduleSettings.runeMode == "Numeric" then
		self.frame.numeric:SetText(tostring(numReady))
		self.frame.numeric:SetTextColor(self:GetColor(self.numericColor))
	else
		for i=1, self.numRunes do
			if i <= numReady then
				if self.moduleSettings.runeMode == "Graphical" then
					self.frame.graphical[i].rune:SetVertexColor(1, 1, 1)
				else
					self:SetCustomColor(i)
				end

				if self.moduleSettings.inactiveDisplayMode == "Hidden" then
					self.frame.graphical[i]:Show()
				end

				if i > self.lastNumReady and self.moduleSettings.flashWhenBecomingReady then
					local fadeInfo={
						mode = "IN",
						timeToFade = self.runeShineFadeSpeed,
						finishedFunc = function() self:ShineFinished(i) end,
						finishedArg1 = i
					}
					UIFrameFade(self.frame.graphical[i].shine, fadeInfo);
				end
			else
				if self.moduleSettings.inactiveDisplayMode == "Darkened" then
					self.frame.graphical[i].rune:SetVertexColor(0, 0, 0)
				elseif self.moduleSettings.inactiveDisplayMode == "Hidden" then
					self.frame.graphical[i]:Hide()
				end
			end
		end
	end

	if self.moduleSettings.pulseWhenFull then
		if numReady > self.lastNumReady and numReady == self.numRunes then
			self:StartRunesFullAnimation()
		elseif numReady < self.numRunes then
			self:StopRunesFullAnimation()
		end
	end

	self.lastNumReady = numReady
	self:UpdateAlpha()

	if (self.moduleSettings.hideBlizz) then
		self:HideBlizz()
	end
end

function IceClassPowerCounter.prototype:StartRunesFullAnimation()
	if not self.frame.anim then
		local grp = self.frame:CreateAnimationGroup()
		grp:SetLooping("BOUNCE")

		-- lots of magic numbers here. i wonder if it's worth making them configurable or not...probably not.
		local scale = grp:CreateAnimation("Scale")
		scale:SetStartDelay(0.5)
		scale:SetEndDelay(0)
		scale:SetDuration(0.75)
		scale:SetOrigin("CENTER", 0, 0)
		scale:SetScale(1.5, 1.5)

		self.frame.anim = grp
	end

	self.frame.anim:Play()
end

function IceClassPowerCounter.prototype:StopRunesFullAnimation()
	if self.frame.anim and self.frame.anim:IsPlaying() then
		self.frame.anim:Stop()
	end
end

function IceClassPowerCounter.prototype:ShineFinished(rune)
	UIFrameFadeOut(self.frame.graphical[rune].shine, self.runeShineFadeSpeed);
end

function IceClassPowerCounter.prototype:GetRuneTexture(rune)
	assert(true, "Must override GetRuneTexture in child classes")
end


function IceClassPowerCounter.prototype:CreateFrame()
	IceClassPowerCounter.super.prototype.CreateFrame(self)

	self.frame:SetFrameStrata("BACKGROUND")
	self.frame:SetHeight(self.runeHeight)
	self.frame:ClearAllPoints()
	self.frame:SetPoint("TOP", self.parent, "BOTTOM", self.moduleSettings.hpos, self.moduleSettings.vpos)

	self:CreateRuneFrame()

	self:SetDisplayMode()
end

function IceClassPowerCounter.prototype:SetDisplayMode()
	if self.moduleSettings.runeMode == "Numeric" then
		self.frame.numeric:Show()
		for i=1, self.numRunes do
			self.frame.graphical[i]:Hide()
		end
	else
		self.frame.numeric:Hide()
		for i=1, self.numRunes do
			self:SetupRuneTexture(i)
			self.frame.graphical[i]:Show()
		end
	end
end

function IceClassPowerCounter.prototype:CreateRuneFrame()
	-- create numeric runes
	self.frame.numeric = self:FontFactory(self.moduleSettings.runeFontSize, self.frame, self.frame.numeric)

	self.frame.numeric:SetWidth(50)
	self.frame.numeric:SetJustifyH("CENTER")

	self.frame.numeric:SetAllPoints(self.frame)
	self.frame.numeric:Hide()

	if (not self.frame.graphical) then
		self.frame.graphical = {}
	end

	for i=1, self.numRunes do
		self:CreateRune(i)
	end
end

function IceClassPowerCounter.prototype:CreateRune(i)
	-- create runes
	if (not self.frame.graphical[i]) then
		self.frame.graphical[i] = CreateFrame("Frame", nil, self.frame)
		self.frame.graphical[i].rune = self.frame.graphical[i]:CreateTexture(nil, "LOW")
		self.frame.graphical[i].rune:SetAllPoints(self.frame.graphical[i])
		self.frame.graphical[i].shine = self.frame.graphical[i]:CreateTexture(nil, "OVERLAY")

		self.frame.graphical[i]:SetWidth(self.runeWidth)
		self.frame.graphical[i]:SetHeight(self.runeHeight)

		self:SetupRuneTexture(i)
		self.frame.graphical[i].rune:SetVertexColor(0, 0, 0)
	end

	self.frame.graphical[i]:SetFrameStrata("BACKGROUND")

	self.frame.graphical[i]:Hide()

	self.frame.graphical[i].shine:SetTexture("Interface\\ComboFrame\\ComboPoint")
	self.frame.graphical[i].shine:SetBlendMode("ADD")
	self.frame.graphical[i].shine:SetTexCoord(0.5625, 1, 0, 1)
	self.frame.graphical[i].shine:ClearAllPoints()
	self.frame.graphical[i].shine:SetPoint("CENTER", self.frame.graphical[i], "CENTER")
	self.frame.graphical[i].shine:SetWidth(self.runeWidth + 25)
	self.frame.graphical[i].shine:SetHeight(self.runeHeight + 10)
	self.frame.graphical[i].shine:Hide()
end

function IceClassPowerCounter.prototype:SetupRuneTexture(rune)
	if not rune or rune < 1 or rune > #self.runeCoords then
		return
	end

	local width = self.runeHeight
	local a,b,c,d = 0, 1, 0, 1
	if self.moduleSettings.runeMode == "Graphical" then
		width = self.runeWidth
		a,b,c,d = unpack(self.runeCoords[rune])
	end

	-- make sure any texture aside from the special one is square and has the proper coordinates
	self.frame.graphical[rune].rune:SetTexCoord(a, b, c, d)
	self.frame.graphical[rune]:SetWidth(width)
	self.frame:SetWidth(width*self.numRunes)
	if self.moduleSettings.displayMode == "Horizontal" then
		self.frame.graphical[rune]:SetPoint("TOPLEFT", (rune-1) * (width-5) + (rune-1) + ((rune-1) * self.moduleSettings.runeGap), 0)
	else
		self.frame.graphical[rune]:SetPoint("TOPLEFT", 0, -1 * ((rune-1) * (self.runeHeight-5) + (rune-1) + ((rune-1) * self.moduleSettings.runeGap)))
	end

	if self.moduleSettings.runeMode == "Graphical" then
		self.frame.graphical[rune].rune:SetTexture(self:GetRuneTexture(rune))
	elseif self.moduleSettings.runeMode == "Graphical Bar" then
		self.frame.graphical[rune].rune:SetTexture(IceElement.TexturePath .. "Combo")
	elseif self.moduleSettings.runeMode == "Graphical Circle" then
		self.frame.graphical[rune].rune:SetTexture(IceElement.TexturePath .. "ComboRound")
	elseif self.moduleSettings.runeMode == "Graphical Glow" then
		self.frame.graphical[rune].rune:SetTexture(IceElement.TexturePath .. "ComboGlow")
	elseif self.moduleSettings.runeMode == "Graphical Clean Circle" then
		self.frame.graphical[rune].rune:SetTexture(IceElement.TexturePath .. "ComboCleanCurves")
	end
end

function IceClassPowerCounter.prototype:GetAlphaAdd()
	return 0.15
end

function IceClassPowerCounter.prototype:SetCustomColor(i)
	local r, g, b = self:GetCustomColor()
	if (self.moduleSettings.gradient) then
		r,g,b = self:GetGradientColor(i)
	end
	self.frame.graphical[i].rune:SetVertexColor(r, g, b)
end

function IceClassPowerCounter.prototype:GetGradientColor(curr)
	local r, g, b = self:GetCustomColor()
	local mr, mg, mb = self:GetCustomMinColor()
	local scale = (curr-1)/(self.numRunes-1)

	if r < mr then
		r = ((r-mr)*scale) + mr
	else
		r = ((mr-r)*scale) + r
	end

	if g < mg then
		g = ((g-mg)*scale) + mg
	else
		g = ((mg-g)*scale) + g
	end

	if b < mb then
		b = ((b-mb)*scale) + mb
	else
		b = ((mb-b)*scale) + b
	end

	return r, g, b
end

function IceClassPowerCounter.prototype:TargetChanged()
	IceClassPowerCounter.super.prototype.TargetChanged(self)
	-- sort of a hack fix...if "ooc" alpha is set to 0, then the runes frame is all jacked up when the user spawns in
	-- need to re-run CreateFrame in order to setup the frame properly. not sure why :(
	self:Redraw()
end

function IceClassPowerCounter.prototype:InCombat()
	IceClassPowerCounter.super.prototype.InCombat(self)
	self:Redraw()
end

function IceClassPowerCounter.prototype:OutCombat()
	IceClassPowerCounter.super.prototype.OutCombat(self)
	self:Redraw()
end

function IceClassPowerCounter.prototype:CheckCombat()
	IceClassPowerCounter.super.prototype.CheckCombat(self)
	self:Redraw()
end

function IceClassPowerCounter.prototype:AlphaPassThroughTarget()
	return self.moduleSettings.hideFriendly and UnitIsFriend("player", "target")
end

function IceClassPowerCounter.prototype:HideBlizz()
	assert(true, "Must override HideBlizz in child classes.")
end

function IceClassPowerCounter.prototype:UseTargetAlpha()
	if not self.moduleSettings.overrideAlpha then
		return false
	end

	if self.bTreatEmptyAsFull then
		return self.lastNumReady > 0
	else
		return self.lastNumReady < self.numRunes
	end
end

